/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package locusts.server.behaviours;

import java.util.ArrayList;
import java.util.List;
import javax.vecmath.Vector2d;
import locusts.common.entities.Actor;
import locusts.common.entities.Entity;
import locusts.common.entities.EntityList;
import locusts.common.entities.EntityType;

/**
 *
 * @author Hamish Morgan
 */
public class SeekMaxEnergy extends AbstractBehaviour {

    protected static final double DEFAULT_MAX_DISTANCE =
            Double.POSITIVE_INFINITY;
    protected EntityType targetType;
    protected List<Entity> targets;
    protected double maxDistance;

    public SeekMaxEnergy(double weight, EntityType targetType,
            double maxDistance) {
        super(weight);
        this.targetType = targetType;
        targets = null;
        this.maxDistance = maxDistance;
    }

    public SeekMaxEnergy(double weight, EntityType targetType) {
        this(weight, targetType, DEFAULT_MAX_DISTANCE);
    }

    public SeekMaxEnergy(EntityType targetType) {
        super();
        this.targetType = targetType;
        targets = null;
        this.maxDistance = DEFAULT_MAX_DISTANCE;
    }

    public void setMaxDistance(double maxDistance) {
        this.maxDistance = maxDistance;
    }

    public double getMaxDistance() {
        return maxDistance;
    }

    @Override
    public void initialize(EntityList entities) {
        super.initialize(entities);
        if (targets == null)
            targets = entities.getAllByType(targetType);
    }

    @Override
    public void clean() {
        super.clean();
        targets = null;
    }

    public Vector2d getSteering(Actor boid) {
        if (targets.size() == 0)
            return new Vector2d();
        final Vector2d[] forces = new Vector2d[targets.size()];
        int i = 0;
        for (Entity t : targets)
            forces[i++] = getSteering(boid, t);
        final Vector2d steering = max(forces);
        steering.scale(getWeight());
        return steering;
    }

    private static final Vector2d max(final Vector2d[] vecs) {
        if (vecs == null || vecs.length == 0)
            throw new IllegalArgumentException();
        if (vecs.length == 1)
            return vecs[0];

        List<Vector2d> argmaxs = new ArrayList<Vector2d>();
        argmaxs.add(vecs[0]);
        double maxLen = vecs[0].lengthSquared();

        for (int i = 1; i < vecs.length; i++) {
            final double len = vecs[i].lengthSquared();
            if (len >= maxLen) {
                if (len != maxLen) {
                    maxLen = len;
                    argmaxs.clear();
                }
                argmaxs.add(vecs[i]);
            }
        }
        return argmaxs.get((int) (Math.random() * argmaxs.size()));
    }

    private Vector2d getSteering(Actor boid, Entity target) {
        Vector2d steering = new Vector2d();
        if (getWeight() == 0)
            return steering;
        // XXX comparing energy against constant (type value)
        if (target == null || target.getEnergy() <= 0)
            return steering;

        double dx = target.getX() - boid.getX();
        double dy = target.getY() - boid.getY();
        double d = (dx * dx + dy * dy);
//        if(d > maxDistance*maxDistance)
//            return steering;

        steering.set(dx * (100 / d), dy * (100 / d));
//        steering.set(dx*(1/d), dy*(1/d));
//
//        steering.scale((double) getWeight());
        steering.scale(1f / (boid.getEnergy() + 1));
        steering.scale(target.getEnergy());

        return steering;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        final SeekMaxEnergy other = (SeekMaxEnergy) obj;
        if (this.targetType != other.targetType &&
                (this.targetType == null || !this.targetType.equals(
                other.targetType)))
            return false;
        return super.equals(obj);
    }

    @Override
    public int hashCode() {
        int hash = super.hashCode();
        hash = 29 * hash + (this.targetType != null ? this.targetType.hashCode()
                : 0);
        return hash;
    }
}
